package com.geor.gcv.core.mat;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import com.geor.gcv.core.model.Area;

public class CellChipperBoxImpl implements CellChipper {

	public CellChipperBoxImpl() {
	}

	@Override
	public List<Area> execute(Mat mat) throws Exception {

		Mat img = new Mat();
		Mat edge = new Mat();
		Mat hierarchy = new Mat();
		try {
			List<Area> areas = new ArrayList<Area>();
			// 高斯滤波去除噪点干扰
			Imgproc.GaussianBlur(mat, img, new Size(3, 3), 0);
			// Mats.releseMat(mat);

			Imgproc.Canny(img, edge, 50, 50);

			// 轮廓
			List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
			Imgproc.findContours(edge, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);

			List<Rect> rects = new ArrayList<Rect>();
			// 查找可能的轮廓
			for (int i = 0; i < contours.size(); i++) {
				Rect rect = Imgproc.boundingRect(contours.get(i));
				rects.add(rect);
				Mats.releseMat(contours.get(i));
			}

			// if (Mats.imshow()) {
			// for (Rect rect : rects) {
			// Point pt1 = new Point(rect.x, rect.y);
			// Point pt2 = new Point(rect.x + rect.width, rect.y + rect.height);
			// Core.rectangle(edge, pt1, pt2, new Scalar(255));
			// }
			// window.addImg(edge);
			// }

			// 区域合并
			mergeWithArea(rects);

			// 垂直合并
			// mergeWithVert(rects);

			// 排序, 从小到大
			Collections.sort(rects, new Comparator<Rect>() {
				@Override
				public int compare(Rect r1, Rect r2) {
					return r1.x - r2.x;
				}
			});

			for (Rect rect : rects) {
				if (rect.width > 0 && rect.height > 0) {
					areas.add(new Area(rect.x, rect.y, rect.width, rect.height));
				}
			}
			return areas;

		} finally {
			Mats.releseMat(hierarchy);
			Mats.releseMat(edge);
			Mats.releseMat(img);
		}
	}

	/**
	 * 区域合并
	 * 
	 * @param rects
	 * @return
	 */
	private List<Rect> mergeWithArea(List<Rect> rects) {

		// 去重复
		Set<Rect> rectSet = new HashSet<Rect>();
		for (Rect rect : rects) {
			if (rect.width == 0 || rect.height == 0)
				continue;
			rectSet.add(rect);
		}
		rects.clear();
		rects.addAll(rectSet);

		for (int i = 0; i < rects.size(); i++) {

			Rect srcRect = rects.get(i);
			// if (srcRect.width == 0 || srcRect.height == 0)
			// continue;

			int mergeNum = 1;
			while (mergeNum > 0) {
				mergeNum = 0;
				for (int j = 0; j < rects.size(); j++) {

					Rect dstRect = rects.get(j);

					if (dstRect.equals(srcRect))
						continue;
					if (dstRect.width == 0 || dstRect.height == 0)
						continue;

					System.out.format("dst rect: %s \n", dstRect.toString());

					int x1 = Math.max(srcRect.x, dstRect.x);
					int y1 = Math.max(srcRect.y, dstRect.y);
					int x2 = Math.min(srcRect.x + srcRect.width, dstRect.x + dstRect.width);
					int y2 = Math.min(srcRect.y + srcRect.height, dstRect.y + dstRect.height);

					// 有效重叠
					if (x2 > x1 && y2 > y1) {
						// 合并src
						srcRect.x = Math.min(srcRect.x, dstRect.x);
						srcRect.y = Math.min(srcRect.y, dstRect.y);
						srcRect.width = Math.max(srcRect.x + srcRect.width, dstRect.x + dstRect.width) - srcRect.x;
						srcRect.height = Math.max(srcRect.y + srcRect.height, dstRect.y + dstRect.height) - srcRect.y;

						dstRect.width = 0;
						dstRect.height = 0;

						mergeNum++;

						// if (Mats.imshow()) {
						// window.clear();
						// Core.rectangle(edge, new
						// org.opencv.core.Point(srcRect.x, srcRect.y),
						// new org.opencv.core.Point(srcRect.x + srcRect.width,
						// srcRect.y + srcRect.height), new Scalar(255));
						// window.addImg(edge);
						// try {
						// Thread.sleep(500);
						// } catch (InterruptedException e) {
						// // TODO Auto-generated catch block
						// e.printStackTrace();
						// }
						// }
					}
				}
			}
		}
		return rects;
	}

	/**
	 * 垂直合并
	 * 
	 * @param rects
	 * @return
	 */
	protected List<Rect> mergeWithVert(List<Rect> rects) {
		for (int i = 0; i < rects.size(); i++) {

			Rect srcRect = rects.get(i);
			if (srcRect.width == 0 || srcRect.height == 0)
				continue;

			int mergeNum = 1;
			while (mergeNum > 0) {
				mergeNum = 0;
				for (int j = 0; j < rects.size(); j++) {

					Rect dstRect = rects.get(j);

					if (dstRect.equals(srcRect))
						continue;
					if (dstRect.width == 0 || dstRect.height == 0)
						continue;

					int x1 = Math.max(srcRect.x, dstRect.x);
					int y1 = Math.max(srcRect.y, dstRect.y);
					int x2 = Math.min(srcRect.x + srcRect.width, dstRect.x + dstRect.width);
					int y2 = dstRect.y + dstRect.height;

					// 有效重叠
					if (x2 > x1 && y2 > y1) {
						// 合并src
						srcRect.x = Math.min(srcRect.x, dstRect.x);
						srcRect.y = Math.min(srcRect.y, dstRect.y);
						srcRect.width = Math.max(srcRect.x + srcRect.width, dstRect.x + dstRect.width) - srcRect.x;
						srcRect.height = Math.max(srcRect.y + srcRect.height, dstRect.y + dstRect.height) - srcRect.y;

						dstRect.width = 0;
						dstRect.height = 0;

						mergeNum++;
					}
				}
			}
		}
		return rects;
	}

	@Override
	public void setParams(Properties properties) {
		// TODO Auto-generated method stub

	}

}
